package org.mmtk.plan.scale.colors;

import org.mmtk.plan.scale.ScaleTrace;
import org.mmtk.plan.scale.utils.ScaleLogger;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.Lock;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.Uninterruptible;

@Uninterruptible
class Helper {
	/**
	 * Static fields and methods
	 */
	private static int helpersTableSize;
	private static int helpersTableIndex;
	private static Helper[] helpersTable;
	private static Lock helpersTableLock;
	private static boolean done;
	
	@Interruptible
	static void init() {
		helpersTableSize = SColors.getHelperThreads();
		helpersTableLock = VM.newLock("helpersTableLock");	
		helpersTable = new Helper[helpersTableSize];
		for(int i = 0; i < helpersTableSize; i++) {
			helpersTable[i] = new Helper();  	
	    }
	}
	
	static boolean isDone() {
		return done;
	}
	
	static void acquireHelpersLock() {
		if(Options.scaleDebug.getValue() > 4) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.writeln("Trying to acquire helpersTableLock");
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
		helpersTableLock.acquire();
		if(Options.scaleDebug.getValue() > 4) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.writeln("Acquired helpersTableLock");
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}
	
	static void releaseHelpersLock() {
		helpersTableLock.release();
		if(Options.scaleDebug.getValue() > 4) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.writeln("Released helpersTableLock");
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}
	
	static void prepareHelpersTable() {
		helpersTableIndex = -1;
		for(int i = 0; i < helpersTableSize; i++) {
			helpersTable[i].prepare();  	
	    }
		done = false;
	}
	
	static Helper getHelper(boolean primary) {
		if (Options.scaleDebug.getValue() > 3) {
			ScaleLogger.acquireLogLock();

			Log.prependThreadId();
			Log.write("Helper.getHelper - enter, primary=");
			Log.write(primary);
			Log.write(", helpersTableIndex=");
			Log.write(helpersTableIndex);
			Log.write(", helpersTableSize=");
			Log.write(helpersTableSize);
			Log.write("\n");
			Log.flush();

			ScaleLogger.releaseLogLock();
		}
		Helper result = null;

		if (!primary) {
			acquireHelpersLock();
			if (helpersTableIndex < helpersTableSize) {
				helpersTableIndex++;
				if (helpersTableIndex < helpersTableSize) {
					result = helpersTable[helpersTableIndex];
				}
			}
			releaseHelpersLock();
		}
		return result;
	}
	
	/**
	 * Private fields and methods
	 */
	private ScaleTrace helperPrivateTrace;
	private CMHelperTraceLocal helperPrivateLocalTrace;
	
	Helper() {
		this.helperPrivateTrace = new ScaleTrace(SColors.metaDataSpace);
		this.helperPrivateLocalTrace = new CMHelperTraceLocal("Helper.localTrace", this.helperPrivateTrace);		
	}
	
	private void prepare() {
		this.helperPrivateTrace.prepare();
		this.helperPrivateLocalTrace.prepare();
	}
	
	void help() {
		if(Options.scaleDebug.getValue() > 3) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write("Helper.help - enter, done=");
			Log.writeln(done);
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
		if(!done) {
			Color color = Color.obtainColor();
			if(color != null) {
				if(Options.scaleDebug.getValue() > 3) {
					ScaleLogger.acquireLogLock();
					Log.prependThreadId();
					Log.write("Helper.help - got color, ColorMark=");
					Log.writeln(color.getColorMark());
					Log.flush();
					ScaleLogger.releaseLogLock();
				}
				this.helperPrivateLocalTrace.helperTrace(color);
			} else {
				if(Options.scaleDebug.getValue() > 3) {
					ScaleLogger.acquireLogLock();
					Log.prependThreadId();
					Log.writeln("Helper.help - no colors, try to call it done");
					Log.flush();
					ScaleLogger.releaseLogLock();
				}
				acquireHelpersLock();
				if(!done) {
					done = true;
					Log.prependThreadId();
					Log.writeln("Helper.help - !!! no more colors");
					Log.flush();
				}
				releaseHelpersLock();
			}
		}
	}
	
	ScaleTrace getTrace() {
		return this.helperPrivateTrace;
	}	
}
